package com.zcaxer.way.viewmodels

import android.app.Application
import android.app.Notification
import android.content.Context
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.Toast
import androidx.databinding.Bindable
import androidx.databinding.Observable
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.zcaxer.way.R
import com.zcaxer.way.data.Binomial
import com.zcaxer.way.data.Building
import com.zcaxer.way.data.BuildingRepository
import com.zcaxer.way.data.Constant
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*


class BuildingViewModel(application: Application) : ObservableViewModel(application),
        AdapterView.OnItemSelectedListener, Observable {
    val app = getApplication<Application>()

    var targetFlag = Constant.SPIRIT
    private val sharedPref = app.getSharedPreferences(app.getString(R.string.preference_file_key), Context.MODE_PRIVATE)
    var servantFlag: Boolean
    var woodServant = sharedPref.getInt(app.getString(R.string.wood_servant_key), 0)
    var grassServant = sharedPref.getInt(app.getString(R.string.grass_servant_key), 0)
    var ironServant = sharedPref.getInt(app.getString(R.string.iron_servant_key), 0)
    var woodFix = sharedPref.getInt(app.getString(R.string.wood_fix_key), 0)
    var grassFix = sharedPref.getInt(app.getString(R.string.grass_fix_key), 0)
    var ironFix = sharedPref.getInt(app.getString(R.string.iron_fix_key), 0)
    var timer = Timer()
    var targetLevel: Int
    private val repository: BuildingRepository
    private val buildings: LiveData<List<Building>>
    val producer: LiveData<List<Building>>
    val upgradeInfos: List<List<Binomial>>

    val timesNeed: MutableLiveData<Long> by lazy {
        MutableLiveData<Long>(sharedPref.getLong(app.getString(R.string.time_need_key), 0L))
    }
    //达到目的所需要的生产次数，等于  所需时间（秒）/10(s)
    val targetDate: MutableLiveData<String> by lazy {
        MutableLiveData<String>(sharedPref.getString(app.getString(R.string.target_date_key), "未"))
    }

    val result:MutableLiveData<String> by lazy {
        MutableLiveData<String>(sharedPref.getString(app.getString(R.string.result_key), "未"))
    }
    private val calendar = Calendar.getInstance()
    private var targetCalendar = Calendar.getInstance()
    var upWoodNeed = 0
    private var getServantFlag = 2
    val targetList = mutableListOf(0, 0, 0)//该目标用于记录目标中洞府产出部分，building.target用于计算升级建筑所需木材
    private val WOODTARGET = 0
    private val GRASSTARGET = 1
    private val IRONTARGET = 2
    private val FOODLIMIT=2000000

    private val upTimes= mutableListOf(0,0,0)
    private var getServantTimes=0


    init {
        repository = BuildingRepository.getInstance(application)
        buildings = repository.buildings
        producer = repository.producers
        upgradeInfos = repository.upgradeInfos
        servantFlag = sharedPref.getBoolean(app.getString(R.string.use_servant_flag_key), false)
        targetLevel = sharedPref.getInt(app.getString(R.string.target_level_key), 1)
    }

    @Bindable
    fun getWoodS(): String {
        return woodServant.toString()
    }

    fun setWoodS(wood: String) {
        if (wood != "") {
            woodServant = wood.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.wood_servant_key), woodServant).apply()
        }
    }

    @Bindable
    fun getWoodF(): String {
        return woodFix.toString()
    }

    fun setWoodF(wood: String) {
        if ( wood != "") {
            woodFix = wood.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.wood_fix_key), woodFix).apply()
        }
    }

    @Bindable
    fun getGrassS(): String {
        return grassServant.toString()
    }

    fun setGrassS(grass: String) {
        if ( grass != "") {
            grassServant = grass.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.grass_servant_key), grassServant).apply()
        }
    }

    @Bindable
    fun getGrassF(): String {
        return grassFix.toString()
    }

    fun setGrassF(grass: String) {
        if ( grass != "") {
            grassFix = grass.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.grass_fix_key), grassFix).apply()
        }
    }

    @Bindable
    fun getIronS(): String {
        return ironServant.toString()
    }

    fun setIronS(iron: String) {
        if (iron != "") {
            ironServant = iron.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.iron_servant_key), ironServant).apply()
        }
    }

    @Bindable
    fun getIronF(): String {
        return ironFix.toString()
    }

    fun setIronF(iron: String) {
        if (iron != "") {
            ironFix = iron.toInt()
            notifyChange()
            sharedPref.edit().putInt(app.getString(R.string.iron_fix_key), ironFix).apply()
        }
    }

    fun saveServantFlag(isChecked: Boolean) {
        sharedPref.edit().putBoolean(app.getString(R.string.use_servant_flag_key), isChecked).apply()
        Log.d("ttt1", "servantFlag saved:$servantFlag")
    }


    private fun setUpgradeInfo(buildingId: Int) {
        viewModelScope.launch {
            producer.value!![buildingId].upgradeInfo = repository.getUpgradeInfo(buildingId)
        }
    }


    fun setLevel(building: Building, level: Int) {
        viewModelScope.launch {
            building.level = level
            building.upgradeInfo = repository.getUpgradeInfo(building.ID)
            building.initBuilding()
            updateBuilding(building)
        }
    }

    fun upgrade(building: Building) {
        setLevel(building, building.level + 1)
    }


    fun updateBuilding(building: Building) {
        viewModelScope.launch {
            repository.updateBuilding(building)
        }
    }

    fun setBuildingWorkers(building: Building, workers: Int) {
        viewModelScope.launch {
            if (workers >= 0 && workers <= building.maxWorkers)
                repository.setWorkers(workers, building.ID)
        }
    }

    fun setBuildingReserve(building: Building, reserve: Int) {
        viewModelScope.launch {
            if (reserve >= 0) {
                repository.setReserve(reserve, building.ID)
                sharedPref.edit().putLong(app.getString(R.string.last_time_key), System.currentTimeMillis() / 10000 * 10000)
                        .apply()
                Log.d("ttt1", building.name + ":" + reserve)
            }
        }
    }

    fun setBuildingTarget(building: Building, taget: Int) {
        viewModelScope.launch {
            if (taget >= 0) {
                repository.setTarget(taget, building.ID)
            }
        }
    }

    fun targetSetLevel(level: Int) {
        targetLevel = level
        sharedPref.edit().putInt(app.getString(R.string.target_level_key), level).apply()
    }

    fun calTarget() {
        val list = producer.value!!
        getServantTimes=0
        targetCalendar = Calendar.getInstance()
        runBlocking {
            setTarget(list)
        }
        getServantFlag = 2
        if (servantFlag) getServant(list)  //如果今日使用道童，则在目标中减去道童产出

        var days = -1
        while (targetCalendar.get(Calendar.DAY_OF_YEAR) > calendar.get(Calendar.DAY_OF_YEAR) + days) {
            //相等时说明需要days天，左边大则说明days天内不能完成,左边小则完成时间应当为targetCalendar当天起床并领取当日道童时
            if (days > -1) {
                getServant(list)
                getFix()
            }//第1次循环无需减去道童产出
            calTargetUpNeed(list)
            calTimesNeed(list, ++days)
        }
        viewModelScope.launch { setWorkersNeed(list) }
        calDateString()
        setResult()
    }

    private fun calTimesNeed(list: List<Building>, days: Int) {
        val totalWoodNeed = upWoodNeed + list[Constant.WOOD].target
        upWoodNeed += targetList[WOODTARGET]
        var consumerFoodNeed = 0
        for (i in list) {
            if (i.ID > 1 && i.target > i.reserve)
                consumerFoodNeed -= (i.target - i.reserve) * i.foodIncrease
        }
        var woodDelta = if (upWoodNeed > list[Constant.WOOD].reserve) totalWoodNeed - list[Constant.WOOD].reserve else 0
        var productFix = 2 * (woodFix) + 4 * (grassFix + ironFix) / 24 / 60 / 6
        timesNeed.value = (2 * woodDelta + consumerFoodNeed) / 1L / (list[Constant.FOOD].workers + productFix)
        targetCalendar.timeInMillis = calendar.timeInMillis + timesNeed.value!! * 10 * 1000

        if (targetCalendar.get(Calendar.HOUR_OF_DAY) < 7 ) {//避开夜间
            targetCalendar.set(targetCalendar.get(Calendar.YEAR), targetCalendar.get(Calendar.MONTH),
                    targetCalendar.get(Calendar.DATE), 7, 0, 0)
            timesNeed.value = (targetCalendar.timeInMillis - calendar.timeInMillis) / 1000 / 10
        }

        if (targetCalendar.get(Calendar.DAY_OF_YEAR) < calendar.get(Calendar.DAY_OF_YEAR) + days) {//此情况完成时间应当为targetCalendar当天起床并领取当日道童时
            targetCalendar = Calendar.getInstance().apply { add(Calendar.DATE, days) }
            targetCalendar.set(targetCalendar.get(Calendar.YEAR), targetCalendar.get(Calendar.MONTH),
                    targetCalendar.get(Calendar.DATE), 9, 30, 0)
            timesNeed.value = (targetCalendar.timeInMillis - calendar.timeInMillis) / 1000 / 10
        }

        if (getServantFlag==0){
            if (targetCalendar.get(Calendar.HOUR_OF_DAY)<10&&targetCalendar.get(Calendar.MINUTE)<31){
                targetCalendar.set(targetCalendar.get(Calendar.YEAR), targetCalendar.get(Calendar.MONTH),
                        targetCalendar.get(Calendar.DATE), 9, 30, 0)
            }
            val timeNeedCalendar=Calendar.getInstance()
            timeNeedCalendar.timeInMillis=targetCalendar.timeInMillis
            timeNeedCalendar.add(Calendar.MINUTE,-150)
            timesNeed.value = (timeNeedCalendar.timeInMillis - calendar.timeInMillis) / 1000 / 10
        }
    }

    private fun calTargetUpNeed(list: List<Building>) {
        //控制该函数至多运行两次
        upWoodNeed = 0
        for (i in 0..2){upTimes[i]=0}
        var woodLevel = list[Constant.WOOD].level
        for (i in list) {
            if (i.target > i.reserve) {//仅当目标超过储量时计算
                var levelNeed = i.level
                while (i.target > Building.calculate(Constant.CAPACITY, upgradeInfos[i.ID], levelNeed)) {
                    if (i.ID != Constant.WOOD) {
                        while (Building.calculate(Constant.UPWOODNEED, upgradeInfos[i.ID], levelNeed) > list[1].capacity) {
                            upWoodNeed += Building.calculate(Constant.UPWOODNEED, upgradeInfos[1], woodLevel)
                            woodLevel++
                        }//当建筑升级所需木材超过木材容量时木材厂需要升级
                    }
                    upWoodNeed += Building.calculate(Constant.CAPACITY, upgradeInfos[i.ID], levelNeed)
                    levelNeed++
                    upTimes[i.ID-1]++
                }//计算容量不足升级建筑所需木材
            }
        }
        upTimes[WOODTARGET]=woodLevel-list[Constant.WOOD].level
    }


    private fun calDateString() {

        var formatter = SimpleDateFormat("YYYY年MM月dd日 HH:mm")
        if (calendar.get(Calendar.YEAR) == targetCalendar.get(Calendar.YEAR))
            formatter = SimpleDateFormat("MM月dd日 HH:mm")
        if (calendar.get(Calendar.MONTH) == targetCalendar.get(Calendar.MONTH))
            formatter = SimpleDateFormat("dd日 HH:mm")
        if (calendar.get(Calendar.DAY_OF_MONTH) == targetCalendar.get(Calendar.DAY_OF_MONTH))
            formatter = SimpleDateFormat("HH:mm")
        targetDate.value = formatter.format(targetCalendar.time)
        with(sharedPref.edit()) {
            putLong(app.getString(R.string.time_need_key), timesNeed.value!!)//error
            putString(app.getString(R.string.target_date_key), targetDate.value)
            apply()
        }
    }

    private fun getServant(list: List<Building>) {
        getServantTimes++
        if (getServantFlag-- > 0) {
            list[Constant.WOOD].target = list[Constant.WOOD].target - woodServant
            list[Constant.GRASS].target = list[Constant.GRASS].target - grassServant
            list[Constant.IRON].target = list[Constant.IRON].target - ironServant
        }
        targetList[WOODTARGET] -= woodServant
        targetList[GRASSTARGET] -= grassServant
        targetList[IRONTARGET] -= ironServant

    }

    private fun getFix() {
        targetList[WOODTARGET] -= woodFix
        targetList[GRASSTARGET] -= grassFix
        targetList[IRONTARGET] -= ironFix
    }

    private fun setTarget(list: List<Building>) {
        if (targetFlag < 3) {
            if (list.size > 0) {
                val info = upgradeInfos[targetFlag + 5]
                targetList[WOODTARGET] = Building.calculate(Constant.UPWOODNEED, info, targetLevel)
                targetList[GRASSTARGET] = Building.calculate(Constant.UPGRASSNEED, info, targetLevel)
                targetList[IRONTARGET] = Building.calculate(Constant.UPIRONNEED, info, targetLevel)
                list[Constant.WOOD].target = targetList[WOODTARGET]
                list[Constant.GRASS].target = targetList[GRASSTARGET]
                list[Constant.IRON].target = targetList[IRONTARGET]
            }
        } else {
            targetList[WOODTARGET] = list[Constant.FOOD].upWoodNeed
            list[Constant.WOOD].target = targetList[WOODTARGET]
            list[Constant.GRASS].target = 0
            list[Constant.IRON].target = 0
        }//targetFlag=4,即目标为升级兽场
    }

    private suspend fun setWorkersNeed(list: List<Building>) {
        if (timesNeed.value != 0L) {
            for (i in 2..3) {
                if (targetList[i - 1] >= list[i].reserve)
                    repository.setWorkersNeed(
                            (targetList[i - 1] - list[i].reserve) / timesNeed.value!!.toInt(), i)
                else repository.setWorkersNeed(0, i)
            }
            if (upWoodNeed >= list[Constant.WOOD].reserve)
                repository.setWorkersNeed(
                        (upWoodNeed - list[Constant.WOOD].reserve) / timesNeed.value!!.toInt(), Constant.WOOD)
            else {
                repository.setWorkersNeed(0, Constant.WOOD)
            }
        }
    }

    fun createTimer() {
        val timeD = System.currentTimeMillis() - sharedPref.getLong(app.getString(R.string.last_time_key), System.currentTimeMillis())
        var t = 1
        if (timeD > 12000) {
            t = (timeD / 10000).toInt()
            Log.d("ttt1", "生产${t}次")
        }
        val period: Long = 10000 //milliseconds=10s
        val task = object : TimerTask() {
            override
            fun run() {
                try {
                    Log.d("ttt1", "ProductWorker Start")
                    product(t + 1)
                    if (t != 0) t = 0
                } catch (e: IOException) {
                    e.printStackTrace()
                    Log.d("ttt1", "ProductWorker failure")
                }
            }
        }
        timer.scheduleAtFixedRate(task, 10000 - timeD % 10000, period)
    }

    fun product(times: Int) {
        viewModelScope.launch {
            var list = repository.producers.value
            while (list == null || list.size != 5) {
                list = repository.producers.value
            }
            var foodIncrease = 0
            for (i in list) foodIncrease += i.foodIncrease * i.workers
            foodIncrease += list[Constant.FOOD].workers
            var t =0
            if (foodIncrease >= 0) t = times
            else t =
                    if (list[Constant.FOOD].reserve / (-1 * foodIncrease) < times)
                        list[Constant.FOOD].reserve / (-1 * foodIncrease)
                    else times
            for (i in list) {
                if (i.ID != Constant.FOOD) {
                    if (i.workers != 0) {
                        setBuildingReserve(i, i.reserve + i.workers * t)
                        Log.d("ttt1", "${i.name}生产$t 次,reserve:${i.reserve} ,workers:${i.workers}")
                    }
                } else {
                    setBuildingReserve(list[Constant.FOOD], list[Constant.FOOD].reserve + foodIncrease * t)
                }
            }

        }
    }

    private fun setResult(){
        var str=""
        for (i in 0..2){
            if (upTimes[i]!=0) str+=
                    "${app.resources.getStringArray(R.array.buildingName)[i+1]}需要升级${upTimes[i]}次,"
        }
        str+="领取道童${getServantTimes}次。"
        result.value=str
        sharedPref.edit().putString(app.getString(R.string.result_key),str).apply()
    }

    /**
     * Callback method to be invoked when the selection disappears from this
     * view. The selection can disappear for instance when touch is activated
     * or when the adapter becomes empty.
     *
     * @param parent The AdapterView that now contains no selected item.
     */
    override fun onNothingSelected(parent: AdapterView<*>?) {
        Log.d("ttt", "selection disappears")
    }

    /**
     *
     * Callback method to be invoked when an item in this view has been
     * selected. This callback is invoked only when the newly selected
     * position is different from the previously selected position or if
     * there was no selected item.
     *
     * Implementers can call getItemAtPosition(position) if they need to access the
     * data associated with the selected item.
     *
     * @param parent The AdapterView where the selection happened
     * @param view The view within the AdapterView that was clicked
     * @param position The position of the view in the adapter
     * @param id The row id of the item that is selected
     */
    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        targetFlag = position
    }


}





